package com.wjk.kylin.core.job;

import com.wjk.kylin.core.constant.ScheduleConstants;
import com.wjk.kylin.core.exception.BusinessException;
import com.wjk.kylin.modules.system.schedule.entity.Schedule;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;

import java.text.ParseException;
import java.util.Date;

/**
 * 定时任务工具类
 *
 * @author ruoyi
 */
@Slf4j
public class ScheduleUtils {

    /**
     * 构建任务触发对象
     * @param scheduleId
     * @param group
     * @return
     */
    public static TriggerKey getTriggerKey(String scheduleId, String group) {
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + scheduleId, group);
    }

    /**
     * 构建任务键对象
     * @param scheduleId
     * @param jobGroup
     * @return
     */
    public static JobKey getJobKey(String scheduleId, String jobGroup) {
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + scheduleId, jobGroup);
    }

    /**
     * 创建定时任务
     * @param scheduler
     * @param schedule
     * @throws RuntimeException
     */
    public static void createScheduleJob(Scheduler scheduler, Schedule schedule) throws RuntimeException {
        try {
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(schedule.getId(), schedule.getJobGroup())).build();

            // 表达式调度构建器
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(schedule.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(schedule.getId(), schedule.getJobGroup()))
                    .withSchedule(cronScheduleBuilder).build();

            // 放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, schedule);

            //构建任务键对象
            JobKey jobKey = getJobKey(schedule.getId(), schedule.getJobGroup());
            // 判断是否存在
            if (scheduler.checkExists(jobKey)) {
                // 防止创建时存在数据问题 先移除，然后在执行创建操作
                scheduler.deleteJob(jobKey);
            }

            scheduler.scheduleJob(jobDetail, trigger);

            // 暂停任务
            if (schedule.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
                scheduler.pauseJob(jobKey);
            }
        } catch (SchedulerException e) {
            log.error("创建定时任务失败", e);
            throw new BusinessException("创建定时任务失败");
        }

    }

    /**
     * 更新任务
     * @param scheduler
     * @param schedule 任务对象
     * @param oldJopGroup 旧任务组名
     */
    public static void updateSchedulerJob(Scheduler scheduler, Schedule schedule, String oldJopGroup) {
        try {
            // 判断旧任务组名的定时任务是否存在、在创建的时候已经判断新的组名的任务是否存在，就不需要在这个判断
            JobKey jobKey = getJobKey(schedule.getId(), oldJopGroup);
            if (scheduler.checkExists(jobKey)) {
                // 防止创建时存在数据问题 先移除，然后在执行创建操作
                scheduler.deleteJob(jobKey);
            }
            createScheduleJob(scheduler, schedule);
        } catch (SchedulerException e) {
            log.error("更新定时任务失败", e);
            throw new BusinessException("更新定时任务失败");
        }

    }


    /**
     * 立即执行任务
     * @param scheduler
     * @param schedule
     */
    public static void run(Scheduler scheduler, Schedule schedule) {
        try {

            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleConstants.TASK_PROPERTIES, schedule);
            scheduler.triggerJob(getJobKey(schedule.getId(), schedule.getJobGroup()), dataMap);

        } catch (SchedulerException e) {
            log.error("立即执行定时任务失败", e);
            throw new BusinessException("立即执行定时任务失败");
        }
    }

    /**
     * 暂停任务
     * @param scheduler
     * @param schedule
     */
    public static void pauseJob(Scheduler scheduler, Schedule schedule) {
        try {

            scheduler.pauseJob(getJobKey(schedule.getId(), schedule.getJobGroup()));

        } catch (SchedulerException e) {
            log.error("暂停定时任务失败", e);
            throw new BusinessException("暂停定时任务失败");
        }
    }

    /**
     * 恢复任务
     * @param scheduler
     * @param schedule
     */
    public static void resumeJob(Scheduler scheduler, Schedule schedule) {
        try {

            scheduler.resumeJob(getJobKey(schedule.getId(), schedule.getJobGroup()));

        } catch (SchedulerException e) {
            log.error("恢复定时任务失败", e);
            throw new BusinessException("恢复定时任务失败");
        }
    }

    /**
     * 删除定时任务
     * @param scheduler
     * @param schedule
     */
    public static void deleteScheduleJob(Scheduler scheduler, Schedule schedule) {
        try {

            scheduler.deleteJob(getJobKey(schedule.getId(), schedule.getJobGroup()));

        } catch (SchedulerException e) {
            log.error("删除定时任务失败", e);
            throw new BusinessException("删除定时任务失败");
        }
    }

    /**
     * 返回下一个执行时间根据给定的Cron表达式
     *
     * @param cronExpression Cron表达式
     * @return Date 下次Cron表达式执行时间
     */
    public static Date getNextExecution(String cronExpression){
        try{
            CronExpression cron = new CronExpression(cronExpression);
            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        }
        catch (ParseException e){
            throw new IllegalArgumentException(e.getMessage());
        }
    }

}